package com.bckj.fastboot.core.extra.txasync.configuration;

import com.bckj.fastboot.core.extra.txasync.TxAsyncAspect;
import com.bckj.fastboot.core.extra.txasync.TxAsyncRunner;
import com.bckj.fastboot.core.extra.txasync.entity.AsyncInvocation;
import com.bckj.fastboot.core.extra.txasync.repo.AsyncInvocationRepository;
import com.bckj.fastboot.core.extra.txasync.service.AsyncInvocationService;
import com.bckj.fastboot.core.extra.txasync.TxAsyncVirtualThreadFactory;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import javax.sql.DataSource;
import java.util.*;

@AutoConfiguration
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "fastboot.tx-async", value = "enabled", havingValue = "true")
public class TxAsyncConfiguration {

    @Bean
    @ConditionalOnMissingBean(AsyncInvocationRepository.class)
    public AsyncInvocationRepository asyncInvocationRepository(DataSource dataSource) {
        return new DefaultAsyncInvocationRepository(dataSource);
    }

    @Bean
    public TxAsyncVirtualThreadFactory txAsyncVirtualThreadFactory() {
        return new TxAsyncVirtualThreadFactory("tx-async");
    }

    @Bean
    public TxAsyncRunner txAsyncRunner(AsyncInvocationRepository asyncInvocationRepository, TxAsyncVirtualThreadFactory txAsyncVirtualThreadFactory) {
        return new TxAsyncRunner(asyncInvocationRepository, txAsyncVirtualThreadFactory);
    }

    @Bean
    public AsyncInvocationService asyncInvocationService(AsyncInvocationRepository asyncInvocationRepository, TxAsyncRunner txAsyncRunner) {
        return new AsyncInvocationService(asyncInvocationRepository, txAsyncRunner);
    }

    @Bean
    public TxAsyncAspect txAsyncAspect(AsyncInvocationService asyncInvocationService) {
        return new TxAsyncAspect(asyncInvocationService);
    }

    public static class DefaultAsyncInvocationRepository implements AsyncInvocationRepository {
        private final Logger log = org.slf4j.LoggerFactory.getLogger(DefaultAsyncInvocationRepository.class);

        private final JdbcClient jdbcClient;

        public DefaultAsyncInvocationRepository(DataSource dataSource) {
            jdbcClient = JdbcClient.create(dataSource);
        }

        @Override
        public boolean save(AsyncInvocation entity) {
            StringBuilder insert = new StringBuilder("INSERT INTO async_invocation(");
            List<Object> params = new ArrayList<>();
            if (entity.getTaskId() != null && !entity.getTaskId().isBlank()) {
                insert.append("task_id, ");
                params.add(entity.getTaskId());
            }
            if (entity.getTaskName() != null && !entity.getTaskName().isBlank()) {
                insert.append("task_name, ");
                params.add(entity.getTaskName());
            }
            if (entity.getMethodName() != null && !entity.getMethodName().isBlank()) {
                insert.append("method_name, ");
                params.add(entity.getMethodName());
            }
            if (entity.getClassName() != null && !entity.getClassName().isBlank()) {
                insert.append("class_name, ");
                params.add(entity.getClassName());
            }
            if (entity.getParams() != null && !entity.getParams().isBlank()) {
                insert.append("params, ");
                params.add(entity.getParams());
            }
            if (entity.getStatus() != null) {
                insert.append("status, ");
                params.add(entity.getStatus());
            }
            if (entity.getInitialInterval() != null) {
                insert.append("initial_interval, ");
                params.add(entity.getInitialInterval());
            }
            if (entity.getMultiplier() != null) {
                insert.append("multiplier, ");
                params.add(entity.getMultiplier());
            }
            if (entity.getTimeout() != null) {
                insert.append("timeout, ");
                params.add(entity.getTimeout());
            }
            if (entity.getRetryCount() != null) {
                insert.append("retry_count, ");
                params.add(entity.getRetryCount());
            }
            if (entity.getMaxAttempt() != null) {
                insert.append("max_attempt, ");
                params.add(entity.getMaxAttempt());
            }
            // 移除最后一个逗号和空格
            if (!params.isEmpty()) {
                insert.delete(insert.length() - 2, insert.length());
            } else {
                return false;
            }
            insert.append(") VALUES (");
            insert.append(String.join(",", Collections.nCopies(params.size(), "?")));
            insert.append(")");
            String sql = insert.toString();
            log.info("sql: {}, params: {}", sql, params);
            // 创建 KeyHolder 用于保存生成的主键
            KeyHolder keyHolder = new GeneratedKeyHolder();
            boolean r = jdbcClient.sql(sql)
                    .params(params)
                    .update(keyHolder, "id") > 0;
            if (r) {
                entity.setId(Objects.requireNonNull(keyHolder.getKey()).longValue());
            }
            return r;
        }

        @Override
        public boolean updateById(AsyncInvocation entity) {
            StringBuilder update = new StringBuilder("UPDATE async_invocation SET ");
            List<Object> params = new ArrayList<>();
            if (entity.getTaskId() != null && !entity.getTaskId().isBlank()) {
                update.append("task_id = ?, ");
                params.add(entity.getTaskId());
            }
            if (entity.getTaskName() != null && !entity.getTaskName().isBlank()) {
                update.append("task_name = ?, ");
                params.add(entity.getTaskName());
            }
            if (entity.getMethodName() != null && !entity.getMethodName().isBlank()) {
                update.append("method_name = ?, ");
                params.add(entity.getMethodName());
            }
            if (entity.getClassName() != null && !entity.getClassName().isBlank()) {
                update.append("class_name = ?, ");
                params.add(entity.getClassName());
            }
            if (entity.getParams() != null && !entity.getParams().isBlank()) {
                update.append("params = ?, ");
                params.add(entity.getParams());
            }
            if (entity.getStatus() != null) {
                update.append("status = ?, ");
                params.add(entity.getStatus());
            }
            if (entity.getInitialInterval() != null) {
                update.append("initial_interval = ?, ");
                params.add(entity.getInitialInterval());
            }
            if (entity.getMultiplier() != null) {
                update.append("multiplier = ?, ");
                params.add(entity.getMultiplier());
            }
            if (entity.getTimeout() != null) {
                update.append("timeout = ?, ");
                params.add(entity.getTimeout());
            }
            if (entity.getRetryCount() != null) {
                update.append("retry_count = ?, ");
                params.add(entity.getRetryCount());
            }
            if (entity.getMaxAttempt() != null) {
                update.append("max_attempt = ?, ");
                params.add(entity.getMaxAttempt());
            }
            if (entity.getIsDeleted() != null) {
                update.append("is_deleted = ?, ");
                params.add(entity.getIsDeleted());
            }
            if (entity.getVersion() != null) {
                update.append("version = ?, ");
                params.add(entity.getVersion());
            }
            if (entity.getUpdateTime() != null) {
                update.append("update_time = ?, ");
                params.add(entity.getUpdateTime());
            }
            // 移除最后一个逗号和空格
            if (!params.isEmpty()) {
                update.delete(update.length() - 2, update.length());
            } else {
                return false;
            }
            // 添加 WHERE 条件
            update.append(" WHERE id = ?");
            params.add(entity.getId());
            String sql = update.toString();
            log.info("sql: {}, params: {}", sql, params);
            return jdbcClient.sql(sql).params(params).update() > 0;
        }

        @Override
        public AsyncInvocation getByTaskId(String taskId) {
            String select = "select * from async_invocation where task_id = ?";
            return jdbcClient.sql(select)
                    .params(taskId).query(AsyncInvocation.class).single();
        }

        @Override
        public void updateStatus(String taskId, Integer status) {
            String update = "update async_invocation set status = ? where task_id = ?";
            log.info("sql: {}, params: {}", update, Arrays.asList(status, taskId));
            jdbcClient.sql(update)
                    .params(status, taskId)
                    .update();
        }
    }
}
